//ssock_udpsock.c
/*
 * Copyright 2010 Samuel C. Payson. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *	
 * THIS SOFTWARE IS PROVIDED BY SAMUEL C. PAYSON ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SAMUEL C. PAYSON OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *	
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Samuel C. Payson.
*/
#include <ssock/ssock_udpsock.h>

inline int atoi(const char * n)
{
	int r = 0;
	while (*n)
	{
		if (*n < '0' || *n > '9')
			return -1;
		r *= 10;
		r += *n - '0';
		++n;
	}
	return r;
}

ssock_udpsocket * ssock_newudp(const char * node, const char * port)
{
	int status;
	struct addrinfo hints, * addrtop;
	ssock_udpsocket * r = malloc(sizeof(ssock_udpsocket));
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;
	if (!node)
		hints.ai_flags = AI_PASSIVE;
	status = getaddrinfo(node, port, &hints, &addrtop);
	if (status)
	{
		free(r);
		return NULL;
	}
	else
	{
		struct addrinfo * seek = addrtop;
		r->udp_fd = -1;
		while (r->udp_fd == -1 && seek)
		{
			r->udp_fd = socket(seek->ai_family, seek->ai_socktype, seek->ai_protocol);
			if (r->udp_fd == -1)
			{
				seek = seek->ai_next;
				continue;
			}
			else
			{ 
				r->udp_ai = *seek;
				r->udp_ai.ai_addr = malloc(r->udp_ai.ai_addrlen);
				memcpy(r->udp_ai.ai_addr, seek->ai_addr, seek->ai_addrlen);
				r->udp_addr = r->udp_ai.ai_addr;
				r->udp_port = (unsigned short)atoi(port);
				if (!node)
				{
					int yes = 1;
					if (setsockopt(r->udp_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
					{
						seek = seek->ai_next;
						continue;
					}
					else if (bind(r->udp_fd, r->udp_ai.ai_addr, r->udp_ai.ai_addrlen) == -1)
					{
						seek = seek->ai_next;
						continue;
					}
				}
				break;
			}
		}
		freeaddrinfo(addrtop);
	}
	return r;
}

int ssock_udpsend(ssock_udpsocket * sock, void * data, size_t sz)
{
	size_t sent = 0;
	int status;
	while (sent < sz)
	{
		status = sendto(sock->udp_fd, data, sz, 0, sock->udp_addr, sock->udp_ai.ai_addrlen);
		if (status == -1)
			return status;
		else
			sent += status;
	}
	return sent;
}


int ssock_udprecv(ssock_udpsocket * sock, void * data, size_t sz)
{
	return recvfrom(sock->udp_fd, data, sz, 0, (struct sockaddr *)&sock->udp_last, &sock->udp_llen);
}

void ssock_freeudp(ssock_udpsocket * sock)
{
	free(sock->udp_addr);
	free(sock);
}
